package server;

import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Date;
import java.util.Locale;
import java.util.Properties;

import javax.jms.JMSException;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueReceiver;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicPublisher;
import javax.jms.TopicSession;
import javax.jms.TopicSubscriber;
import javax.naming.Context;
import javax.naming.InitialContext;

public class TopicBasedServer {
	
	private static final int BAR_LENGTH = 20;
	private static final String ADD = " <-- ";
	private static final String REMOVE = " --> ";
	
	private long id = 0;
	
	private int lastLoadReceived = 0;
	private int runningJobs = 0;
	private int executedJobs = 0;
	private int maxJobs = 0;
	
	private boolean isRunning = true;
	private boolean isAlone = true;
	
	private Topic topic;
	private Queue queue;
	
	private QueueConnection qc;
	private TopicConnection tc;
	
	private QueueReceiver rec;
	
	private TopicSession ts_pub;
	private TopicSession ts_sub;
	
	private TopicPublisher tpub;
	private TopicSubscriber tsub;
	
	private TopicBasedController tbcon;
	private TopicBasedLogger logger;
	
	
	public TopicBasedServer(int maxJobs) throws Exception {
		Properties properties = new Properties();
		properties.put("java.naming.factory.initial", "fr.dyade.aaa.jndi2.client.NamingContextFactory");
		properties.put("java.naming.factory.host", "localhost");
		properties.put("java.naming.factory.port", "16400");
		Context ictx = new InitialContext(properties);
		
		this.queue = (Queue) ictx.lookup("queue");
		QueueConnectionFactory qcf = (QueueConnectionFactory) ictx.lookup("qcf");

		this.topic = (Topic) ictx.lookup("topic");
		TopicConnectionFactory tcf = (TopicConnectionFactory) ictx.lookup("tcf");
		ictx.close();

		//connections	
		this.qc = qcf.createQueueConnection();
		this.tc = tcf.createTopicConnection();
		this.maxJobs = maxJobs;
		
		this.id = new Date().getTime();
		
		logger = new TopicBasedLogger(this);
		
	}
	
	public void shout() throws JMSException {
		TextMessage message = ts_pub.createTextMessage();
		message.setText(String.valueOf(runningJobs));					
		
		tpub.publish(message);
		ts_pub.commit();	
	}
	
	public synchronized void verify() throws JMSException {
		if(isAlone && runningJobs < maxJobs){
			if(!isRunning) {
				qc.start();
				System.out.print(">> restart: server alone - \n");
				isRunning = true;
			}
		}else{
			if(runningJobs > lastLoadReceived || runningJobs >= maxJobs) {
				if(isRunning) {
					qc.stop();
					System.out.print(">> stop: someone has " + lastLoadReceived + " jobs - \n");
					//System.out.println("i have " + runningJobs + " jobs - overload");
					isRunning = false;
				}
			} else {
				if(!isRunning) {
					qc.start();
					System.out.print(">> restart: someone has " + lastLoadReceived + " jobs - \n");
					//System.out.println("i have " + runningJobs + " jobs - underload");
					isRunning = true;
				}
			}
		}
	}

	public void printLoad(String job, String action) {
		DecimalFormat fmt = new DecimalFormat("0.0", new DecimalFormatSymbols(Locale.UK));
		
		double perc = (double) runningJobs / maxJobs;
		int fill = (int) ((float) BAR_LENGTH*perc);
		
		System.out.print(fmt.format(perc*100.0)+"% \t[");
		for(int i=0; i<fill; i++) {
			if(i==BAR_LENGTH-1) {
				System.out.print("#]");
			} else {
				System.out.print("#|");
			}
		}
		
		for(int i=0; i<BAR_LENGTH-fill; i++) {
			if(i==BAR_LENGTH-fill-1) {
				System.out.print(" ]");
			} else {
				System.out.print(" |");
			}
		}
		System.out.print(action+job);
		System.out.println(" [queued:" + runningJobs + " - accepted:" + executedJobs + "]");
	}
	
	public synchronized void addJob(String job) {
		runningJobs++;
		executedJobs++;
		printLoad(job, ADD);
	}
	
	public synchronized void removeJob(String job) {
		runningJobs--;
		printLoad(job, REMOVE);
	}
	
	public int getRunningJobs() {
		return runningJobs;
	}
	
	public void start() throws Exception {
		
		//controller
		this.tbcon = new TopicBasedController(this);
		this.tbcon.start();
		
		//topic		
		this.ts_sub = this.tc.createTopicSession(true, Session.SESSION_TRANSACTED);
		this.ts_pub = this.tc.createTopicSession(true, Session.SESSION_TRANSACTED);
		
		this.tpub = this.ts_pub.createPublisher(topic);
		this.tsub = this.ts_sub.createSubscriber(this.topic, null, true);
		TopicBasedListener ll = new TopicBasedListener(this);
		
		this.tsub.setMessageListener(ll);
		this.tc.start();
		
		//queue
		QueueSession qs = this.qc.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
		
		this.rec = qs.createReceiver(this.queue);
		
	    this.qc.start();
	    
	    this.logger.start();
	    
	}
	
	public void interruptTBController(){
		tbcon.interrupt();
	}
	
	public int getMaxJobs(){
		return this.maxJobs;		
	}
	
	public int getLastLoadReceived(){
		return this.lastLoadReceived;
	}
	
	public synchronized void setLastLoadReceived(int lastLoadReceived){
		this.lastLoadReceived = lastLoadReceived;
	}
	
	public QueueConnection getQueueConnection(){
		return this.qc;
	}
	
	public QueueReceiver getQueueReceiver(){
		return this.rec;
	}
	
	public synchronized void setIsAlone(boolean isAlone){
		this.isAlone = isAlone;		
	}
	
	public boolean isAlone(){
		return this.isAlone;		
	}
	
	public long getId() {
		return this.id;
	}
	
}

